package com.letoken.platform.app.service.transfer;

import cn.hutool.extra.spring.SpringUtil;
import com.letoken.platform.repo.performance.TeamPerformanceService;
import com.letoken.platform.app.util.LeTokenUtil;
import com.letoken.platform.pub.constant.ConstantsValue;
import com.letoken.platform.pub.constant.LetokenTokenConstant;
import com.letoken.platform.pub.constant.LetokenUserConstant;
import com.letoken.platform.pub.constant.RedisKeyConstant;
import com.letoken.platform.pub.enums.*;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.repo.admin.po.Withdraw;
import com.letoken.platform.repo.app.dao.AppUserAssetsHistoryMapper;
import com.letoken.platform.repo.app.dao.AppUserWalletMapper;
import com.letoken.platform.repo.app.manager.AppUserWalletManager;
import com.letoken.platform.repo.app.po.AppUserAssetsHistory;
import com.letoken.platform.repo.app.po.AppUserWallet;
import com.letoken.platform.repo.app.redis.RedisConfig;
import com.letoken.platform.repo.app.req.token.WithdrawUsdtReq;
import com.letoken.platform.repo.utlis.GasUtils;
import com.letoken.platform.repo.utlis.LocalBlockUtils;
import com.letoken.platform.repo.utlis.lock.Lock;
import com.letoken.platform.repo.utlis.lock.LockCategoryEnum;
import com.letoken.platform.repo.web3.util.EvmUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;

@Component
@Slf4j
public class IntroversionService extends AbstractTransfer{
    @Resource
    AppUserWalletManager walletManager;
    @Resource
    AppUserAssetsHistoryMapper appUserAssetsHistoryMapper;
    @Resource
    AppUserWalletMapper appUserWalletMapper;

    @Autowired
    GasUtils gasUtils;

    @Resource
    private RedisConfig redisConfig;

    @Resource
    private TeamPerformanceService teamPerformanceService;

    @Override
    public ITransfer checkout(WithdrawUsdtReq req) {
        super.checkout(req);
        // lp不支持内转
        if (req.getTokenId().equals(LetokenTokenTypeEnum.LP.getId())){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_058);
        }
//        if (req.getTokenId().equals(LetokenTokenTypeEnum.CAPA.getId())){
//            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_MALL_SMS_058);
//        }

        //平台内互转金额要大于0
        if (req.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_012);
        }
        LocalBlockUtils.AddressPo fromPo = LocalBlockUtils.obtainAddress(LocalBlockUtils.ObtainAddressDto.builder()
                .userId(req.getUser().getId())
                .coinId(LetokenTokenTypeEnum.USDT.getId())
                .build());
        req.setFromAddress(fromPo.getAddr());


        String leUserAddressOrUserId = req.getUserAddressOrUserId();
        //平台转账 如果传的是钱包地址
        if (!StringUtils.startsWithIgnoreCase(leUserAddressOrUserId, LetokenUserConstant.USER_ID_PREFIX)) {
            //验证地址有效性
            if (!EvmUtil.checkAddressChecksum(leUserAddressOrUserId)) {
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_015);
            }
            req.setToAddress(leUserAddressOrUserId);
            // todo 刚上线一段时间内 可能会出现接收方地址不存在
            LocalBlockUtils.AddressPo po = LocalBlockUtils.obtainUserId(LocalBlockUtils.ObtainUserIdDto.builder()
                    .addr(leUserAddressOrUserId)
                    .coinId(LetokenTokenTypeEnum.USDT.getId())
                    .build());
            if (!po.isSuccess()){
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_004);
            }

            req.setToUserId(po.getUserId());
        }else {
            // 如果是le开头 返回用户id todo 如果通过用户id内转，不需要找接收人地址
            String userAddressOrUserId = LeTokenUtil.appUserIdStartsWithLe(leUserAddressOrUserId);
            req.setToUserId(Integer.valueOf(userAddressOrUserId));
            req.setToAddress("");
        }
        // 转出方与转入方不能相同
        if (req.getToUserId().equals(req.getUser().getId())) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_003);
        }
        //提现金额是否大于自己的金额 大于就报错 > 2023-03-26 add by usher
        AppUserWallet fromAppUserWallet = walletManager.queryWalletByToken(req.getUser().getId(), req.getToken().getId());
        AppUserWallet toUserWallet = walletManager.queryWalletByToken(req.getToUserId(),req.getToken().getId());
        if (ObjectUtils.isEmpty(fromAppUserWallet) || ObjectUtils.isEmpty(toUserWallet)){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_004);
        }
        req.setWalletId(fromAppUserWallet.getId());
        req.setToWalletId(toUserWallet.getId());
        return SpringUtil.getBean(IntroversionService.class);

    }

    @Override
    @Lock(ce = LockCategoryEnum.ID,value = "WALLET:MODIFY",field = "walletId",expire = 3l)
    @Transactional(rollbackFor = Exception.class)
    public void transfer(WithdrawUsdtReq req) {
        AppUserWallet fromAppUserWallet = appUserWalletMapper.selectById(req.getWalletId());

        if (req.getAmount().compareTo(fromAppUserWallet.getAvailableBalance()) > 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_007);
        }
        AppUserWallet toUserWallet = appUserWalletMapper.selectForUpdate(req.getToWalletId());
        if (toUserWallet.getAvailableBalance().compareTo(BigDecimal.ZERO) < 0){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_021);
        }

        // 增加 手续费 扣减
        BigDecimal fee = gasUtils.getFee(BigDecimal.ZERO,1);
        log.info("手续费金额:"+fee);
        if(!req.getTokenId().equals(LetokenTokenTypeEnum.CAP.getId())){
            log.info("~~~~~~~~~用户id:"+fromAppUserWallet.getUserId()+"可用余额:"+fromAppUserWallet.getAvailableBalance()+"手续费加转账金额:"+fee.add(req.getAmount()));
            gasUtils.systemGasFee(req.getUser().getId(),fee);
        }else {
            if(fromAppUserWallet.getAvailableBalance().compareTo(fee.add(req.getAmount()))<=0){
                req.setAmount(req.getAmount().subtract(fee));
            }
            log.info("用户id:"+fromAppUserWallet.getUserId()+"可用余额:"+fromAppUserWallet.getAvailableBalance()+"手续费加转账金额:"+fee.add(req.getAmount()));
            gasUtils.systemGasFee(req.getUser().getId(),fee);
        }

        Withdraw withdraw = new Withdraw();
        withdraw.setSn(req.getUuId());
        withdraw.setUserId(req.getUser().getId());
        withdraw.setTokenId(req.getToken().getId());
        withdraw.setChainId(req.getToken().getChainId());
        withdraw.setFeeAmount(fee);
        log.info("插入到数据库金额:"+withdraw.getFeeAmount());
        //收款地址
        withdraw.setAddress(req.getUserAddressOrUserId());
        withdraw.setToId(req.getToUserId());
        withdraw.setAmount(req.getAmount());
        //withdraw.setFeeAmount(BigDecimal.ZERO);
        // 平台互转 transfer_mode 为0 反之为1
        withdraw.setTransferMode(req.getTransferMode());
        withdraw.setTxid("");
        withdraw.setStatus(TokenTransactionStatusEnum.PENDING.getValue());
        withdraw.setIsAuto(true);
        withdraw.setCreateTime(req.getNow());
        withdraw.setApprovedDate(req.getNow());
        withdraw.setResponse("");
        //自动审核
        withdraw.setApprovedBy(LetokenTokenConstant.SYSTEM_AUTO);
        withdraw.setRemark("");
        //后续添加 开始
        withdraw.setRealAmount(withdraw.getAmount());
        withdraw.setGasUsed(BigDecimal.ZERO);
        // 结束
        withdraw.setApprovedState(TokenApprovedStateEnum.ADOPT.getValue());
        withdraw.setConfirms(0);
        withdraw.setProcessTime(LocalDateTime.now());
        withdraw.setStatus(TokenTransactionStatusEnum.COMPLETED.getValue());
        withdraw.insert();

        fromAppUserWallet = appUserWalletMapper.selectForUpdate(req.getWalletId());
        String key = String.format(RedisKeyConstant.UPDATE_WALLET_INFO,fromAppUserWallet.getUserId());
        RedissonClient redissonClient = redisConfig.getRedisson();
        RLock rlock = redissonClient.getLock(key);
        AppUserWallet auw = new AppUserWallet();
        AppUserWallet toAuw = new AppUserWallet();
        try {

        auw.setId(fromAppUserWallet.getId());
        auw.setAvailableBalance(fromAppUserWallet.getAvailableBalance().subtract(req.getAmount()));
        appUserWalletMapper.updateById(auw);


        toAuw.setId(toUserWallet.getId());
        toAuw.setUserId(toUserWallet.getUserId());
        toAuw.setAvailableBalance(toUserWallet.getAvailableBalance().add(req.getAmount()));
        appUserWalletMapper.updateById(toAuw);
        }finally {
            if (rlock != null && rlock.isHeldByCurrentThread()) {
                rlock.unlock();
            }
        }

        // 修改转出用户资产记录
        TokenTransactionTypeEnum type = TokenTransactionTypeEnum.WITHDRAW;
        AppUserAssetsHistory data = new AppUserAssetsHistory();
        data.setType(type.getValue());
        data.setUserId(withdraw.getUserId());
        data.setTokenId(withdraw.getTokenId());
        data.setTokenCurrency(req.getToken().getTokenCurrency());
        data.setSn(withdraw.getSn());
        data.setStatus(withdraw.getStatus());
        data.setCreateTime(req.getNow());
        data.setAmount(withdraw.getAmount().negate());
        data.setToAddress(StringUtils.isNotBlank(req.getToAddress()) ? req.getToAddress() :  (LetokenUserConstant.USER_ID_PREFIX + withdraw.getToId()));
        data.setFromUserName(req.getUser().getUserName());
        data.setFromAddress(req.getFromAddress());
        data.setBalance(auw.getAvailableBalance());
        appUserAssetsHistoryMapper.insert(data);

        type = TokenTransactionTypeEnum.RECHARGE;
        data = new AppUserAssetsHistory();
        data.setType(type.getValue());
        data.setUserId(withdraw.getToId());
        data.setTokenId(withdraw.getTokenId());
        data.setTokenCurrency(req.getToken().getTokenCurrency());
        data.setSn(getOtherSeq("WI",req.getUuId()));
        data.setFromUserName(req.getUser().getUserName());
        data.setFromAddress(req.getFromAddress());
        data.setStatus(withdraw.getStatus());
        data.setCreateTime(req.getNow());
        data.setAmount(withdraw.getAmount());
        data.setToAddress(req.getToAddress());
        data.setBalance(toAuw.getAvailableBalance());
        appUserAssetsHistoryMapper.insert(data);


        if(LetokenTokenTypeEnum.CAP.getId().equals(withdraw.getTokenId())){
            teamPerformanceService.add(TeamPerformanceTypeEnum.CapInternalTransferOut, fromAppUserWallet.getUserId(), req.getAmount());
            teamPerformanceService.add(TeamPerformanceTypeEnum.CapInternalTransfer, toUserWallet.getUserId(), req.getAmount());
        } else if(LetokenTokenTypeEnum.LET.getId().equals(withdraw.getTokenId())) {
            teamPerformanceService.add(TeamPerformanceTypeEnum.UsdtInternalTransferOut, fromAppUserWallet.getUserId(), req.getAmount());
            teamPerformanceService.add(TeamPerformanceTypeEnum.UsdtInternalTransfer, toUserWallet.getUserId(), req.getAmount());
        }
    }


    @Override
    public ITransfer choose(Integer mode) {
        if (ConstantsValue.I_0.equals(mode))
            return this;
        else
            return null;
    }
}
